/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.web.service.support;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import net.sf.cglib.beans.BeanMap;
import org.vacoor.nothing.common.json.Jacksons;
import org.vacoor.nothing.common.reflect.GenericBeanSupport;
import org.vacoor.nothing.common.util.Castor;
import org.vacoor.nothing.common.util.IOUtils;
import org.vacoor.nothing.common.util.StringUtils;
import org.vacoor.nothing.common.util.Throwables;

import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author vacoor
 */
public abstract class JsonFileManager<T> extends GenericBeanSupport<T> {
    protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    protected final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    protected final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    protected final Class<T> type;
    protected final File file;
    protected final String keyProperty;
    protected Map<String, T> cache;

    public JsonFileManager(File file, String keyProperty) {
        this.file = file;
        this.keyProperty = keyProperty;
        this.type = getActualTypeArgument();
        refresh();
    }

    public List<T> findAll() {
        System.out.println("find all");
        List<T> v = Lists.newArrayList();
        for (Map.Entry<String, T> kv : cache.entrySet()) {
            v.add(kv.getValue());
        }
        return v;
    }

    public T find(String key) {
        System.out.println("find");
        readLock.lock();
        try {
            return cache.get(key);
        } finally {
            readLock.unlock();
        }
    }

    public void update(T t) {
        writeLock.lock();
        try {
            BeanMap beanMap = BeanMap.create(t);
            String key = Castor.asString(beanMap.get(keyProperty));
            T orig = cache.get(key);
            BeanMap.create(orig).putAll(beanMap);
        } finally {
            writeLock.unlock();
        }
    }

    public void save(T t) {
        writeLock.lock();
        try {
            BeanMap beanMap = BeanMap.create(t);
            String key = Castor.asString(beanMap.get(keyProperty));
            cache.put(key, t);
        } finally {
            writeLock.unlock();
        }
    }

    // 刷新当前缓存
    protected void refresh() {
        writeLock.lock();
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            String json = IOUtils.readText(file);
            if (StringUtils.hasText(json)) {
                cache = Jacksons.deserialize(json, Jacksons.constructGenericType(LinkedHashMap.class, String.class, type));
            } else {
                cache = Maps.newLinkedHashMap();
            }
        } catch (IOException e) {
            cache = Maps.newLinkedHashMap();
            Throwables.unchecked(e);
        } finally {
            writeLock.unlock();
        }
    }

    protected void flush() {
        try {
            Jacksons.serialize(Files.newOutputStreamSupplier(file).getOutput(), cache);
        } catch (IOException e) {
            Throwables.unchecked(e);
        }
    }
}
